﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Runtime.Serialization
Imports System.Security.Permissions
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.ComponentModel
    <Serializable> _
    Public NotInheritable Class DependencyProperty
        Implements ISerializable
        ' Methods
        Private Sub New(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type, ByVal defaultMetadata As PropertyMetadata, ByVal validatorType As Type, ByVal isRegistered As Boolean)
            Me.name = name
            Me.propertyType = propertyType
            Me.ownerType = ownerType
            Me.validatorType = validatorType
            Me.isRegistered = isRegistered
            Me.defaultMetadata = defaultMetadata
            Me.defaultMetadata.Seal(Me, propertyType)
            Me.isEvent = (GetType(Delegate).IsAssignableFrom(Me.propertyType) AndAlso ((Me.defaultMetadata Is Nothing) OrElse (CByte((Me.defaultMetadata.Options And DependencyPropertyOptions.DelegateProperty)) = 0)))
        End Sub

        Friend Shared Function FromKnown(ByVal byteVal As Byte) As DependencyProperty
            If (DependencyProperty.knownProperties(byteVal) Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_NotRegisteredAs", New Object() { DependencyProperty.knownProperties(byteVal).dependencyProperty.ToString }))
            End If
            Return DependencyProperty.knownProperties(byteVal).dependencyProperty
        End Function

        Public Shared Function FromName(ByVal propertyName As String, ByVal ownerType As Type) As DependencyProperty
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            If (ownerType Is Nothing) Then
                Throw New ArgumentNullException("ownerType")
            End If
            Dim property As DependencyProperty = Nothing
            Do While (([property] Is Nothing) AndAlso (Not ownerType Is Nothing))
                RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle)
                Dim key As Integer = (propertyName.GetHashCode Xor ownerType.GetHashCode)
                SyncLock DirectCast(DependencyProperty.dependencyProperties, ICollection).SyncRoot
                    If DependencyProperty.dependencyProperties.ContainsKey(key) Then
                        [property] = DependencyProperty.dependencyProperties.Item(key)
                    End If
                End SyncLock
                ownerType = ownerType.BaseType
            Loop
            Return [property]
        End Function

        Public Shared Function FromType(ByVal ownerType As Type) As IList([Of] DependencyProperty)
            If (ownerType Is Nothing) Then
                Throw New ArgumentNullException("ownerType")
            End If
            RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle)
            Dim list As New List([Of] DependencyProperty)
            SyncLock DirectCast(DependencyProperty.dependencyProperties, ICollection).SyncRoot
                Dim property As DependencyProperty
                For Each property In DependencyProperty.dependencyProperties.Values
                    If (TypeProvider.IsSubclassOf(ownerType, [property].ownerType) OrElse (ownerType Is [property].ownerType)) Then
                        list.Add([property])
                    End If
                Next
            End SyncLock
            Return list.AsReadOnly
        End Function

        Private Shared Function GetDefaultValue(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type) As Object
            If (name Is Nothing) Then
                Throw New ArgumentNullException("name")
            End If
            If (name.Length = 0) Then
                Throw New ArgumentException(SR.GetString("Error_EmptyArgument"), "name")
            End If
            If (propertyType Is Nothing) Then
                Throw New ArgumentNullException("propertyType")
            End If
            If (ownerType Is Nothing) Then
                Throw New ArgumentNullException("ownerType")
            End If
            Dim obj2 As Object = Nothing
            If propertyType.IsValueType Then
                Try 
                    If propertyType.IsEnum Then
                        Dim values As Array = Enum.GetValues(propertyType)
                        If (values.Length > 0) Then
                            Return values.GetValue(0)
                        End If
                        Return Activator.CreateInstance(propertyType)
                    End If
                    obj2 = Activator.CreateInstance(propertyType)
                Catch obj1 As Object
                End Try
            End If
            Return obj2
        End Function

        Public Overrides Function GetHashCode() As Integer
            Return (Me.name.GetHashCode Xor Me.ownerType.GetHashCode)
        End Function

        Public Shared Function Register(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type) As DependencyProperty
            Return DependencyProperty.ValidateAndRegister(name, propertyType, ownerType, Nothing, Nothing, True)
        End Function

        Public Shared Function Register(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type, ByVal defaultMetadata As PropertyMetadata) As DependencyProperty
            Return DependencyProperty.ValidateAndRegister(name, propertyType, ownerType, defaultMetadata, Nothing, True)
        End Function

        Friend Shared Sub RegisterAsKnown(ByVal dependencyProperty As DependencyProperty, ByVal byteVal As Byte, ByVal propertyValidity As PropertyValidity)
            If (dependencyProperty Is Nothing) Then
                Throw New ArgumentNullException("dependencyProperty")
            End If
            If (Not DependencyProperty.knownProperties(byteVal) Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_AlreadyRegisteredAs", New Object() { DependencyProperty.knownProperties(byteVal).dependencyProperty.ToString }))
            End If
            dependencyProperty.KnownIndex = byteVal
            DependencyProperty.knownProperties(byteVal) = New KnownDependencyProperty(dependencyProperty, propertyValidity)
        End Sub

        Public Shared Function RegisterAttached(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type) As DependencyProperty
            Return DependencyProperty.ValidateAndRegister(name, propertyType, ownerType, Nothing, Nothing, False)
        End Function

        Public Shared Function RegisterAttached(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type, ByVal defaultMetadata As PropertyMetadata) As DependencyProperty
            Return DependencyProperty.ValidateAndRegister(name, propertyType, ownerType, defaultMetadata, Nothing, False)
        End Function

        Public Shared Function RegisterAttached(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type, ByVal defaultMetadata As PropertyMetadata, ByVal validatorType As Type) As DependencyProperty
            If (validatorType Is Nothing) Then
                Throw New ArgumentNullException("validatorType")
            End If
            If Not GetType(Validator).IsAssignableFrom(validatorType) Then
                Throw New ArgumentException(SR.GetString("Error_ValidatorTypeIsInvalid"), "validatorType")
            End If
            Return DependencyProperty.ValidateAndRegister(name, propertyType, ownerType, defaultMetadata, validatorType, False)
        End Function

        <SecurityPermission(SecurityAction.LinkDemand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
        Private Sub System.Runtime.Serialization.ISerializable.GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
            info.AddValue("type", Me.ownerType)
            info.AddValue("name", Me.name)
            info.SetType(GetType(DependencyPropertyReference))
        End Sub

        Public Overrides Function ToString() As String
            Return Me.name
        End Function

        Private Shared Function ValidateAndRegister(ByVal name As String, ByVal propertyType As Type, ByVal ownerType As Type, ByVal defaultMetadata As PropertyMetadata, ByVal validatorType As Type, ByVal isRegistered As Boolean) As DependencyProperty
            If (name Is Nothing) Then
                Throw New ArgumentNullException("name")
            End If
            If (name.Length = 0) Then
                Throw New ArgumentException(SR.GetString("Error_EmptyArgument"), "name")
            End If
            If (propertyType Is Nothing) Then
                Throw New ArgumentNullException("propertyType")
            End If
            If (ownerType Is Nothing) Then
                Throw New ArgumentNullException("ownerType")
            End If
            Dim field As FieldInfo = Nothing
            Dim flag As Boolean = (GetType(Delegate).IsAssignableFrom(propertyType) AndAlso ((defaultMetadata Is Nothing) OrElse (CByte((defaultMetadata.Options And DependencyPropertyOptions.DelegateProperty)) = 0)))
            If ((flag AndAlso (Not defaultMetadata Is Nothing)) AndAlso defaultMetadata.IsMetaProperty) Then
                Throw New ArgumentException(SR.GetString("Error_DPAddHandlerMetaProperty"), "defaultMetadata")
            End If
            If flag Then
                field = ownerType.GetField((name & "Event"), (BindingFlags.GetProperty Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.DeclaredOnly)))))
            Else
                field = ownerType.GetField((name & "Property"), (BindingFlags.GetProperty Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.DeclaredOnly)))))
            End If
            If (field Is Nothing) Then
                Throw New ArgumentException(SR.GetString(If(flag, "Error_DynamicEventNotSupported", "Error_DynamicPropertyNotSupported"), New Object() { ownerType.FullName, name }), "ownerType")
            End If
            Dim metadata As PropertyMetadata = Nothing
            If (defaultMetadata Is Nothing) Then
                metadata = New PropertyMetadata(DependencyProperty.GetDefaultValue(name, propertyType, ownerType))
            Else
                metadata = defaultMetadata
                If (metadata.DefaultValue Is Nothing) Then
                    metadata.DefaultValue = DependencyProperty.GetDefaultValue(name, propertyType, ownerType)
                End If
            End If
            Dim property As New DependencyProperty(name, propertyType, ownerType, metadata, validatorType, isRegistered)
            SyncLock DirectCast(DependencyProperty.dependencyProperties, ICollection).SyncRoot
                If DependencyProperty.dependencyProperties.ContainsKey([property].GetHashCode) Then
                    Throw New InvalidOperationException(SR.GetString("Error_DPAlreadyExist", New Object() { name, ownerType.FullName }))
                End If
                DependencyProperty.dependencyProperties.Add([property].GetHashCode, [property])
            End SyncLock
            Return [property]
        End Function


        ' Properties
        Public ReadOnly Property DefaultMetadata As PropertyMetadata
            Get
                Return Me.defaultMetadata
            End Get
        End Property

        Public ReadOnly Property IsAttached As Boolean
            Get
                Return Not Me.isRegistered
            End Get
        End Property

        Public ReadOnly Property IsEvent As Boolean
            Get
                Return Me.isEvent
            End Get
        End Property

        Friend ReadOnly Property IsKnown As Boolean
            Get
                Return (Me.knownIndex <> 0)
            End Get
        End Property

        Friend Property KnownIndex As Byte
            Get
                Return Me.knownIndex
            End Get
            Set(ByVal value As Byte)
                Me.knownIndex = value
            End Set
        End Property

        Public ReadOnly Property Name As String
            Get
                Return Me.name
            End Get
        End Property

        Public ReadOnly Property OwnerType As Type
            Get
                Return Me.ownerType
            End Get
        End Property

        Public ReadOnly Property PropertyType As Type
            Get
                Return Me.propertyType
            End Get
        End Property

        Public ReadOnly Property ValidatorType As Type
            Get
                Return Me.validatorType
            End Get
        End Property

        Friend ReadOnly Property Validity As PropertyValidity
            Get
                If Not Me.IsKnown Then
                    Return PropertyValidity.Always
                End If
                Return DependencyProperty.knownProperties(Me.knownIndex).propertyValidity
            End Get
        End Property


        ' Fields
        Private defaultMetadata As PropertyMetadata
        Private Shared dependencyProperties As IDictionary([Of] Integer, DependencyProperty) = New Dictionary([Of] Integer, DependencyProperty)
        <NonSerialized> _
        Private isEvent As Boolean
        Private isRegistered As Boolean
        Private knownIndex As Byte
        Private Shared knownProperties As KnownDependencyProperty() = New KnownDependencyProperty(&H100  - 1) {}
        Private name As String = String.Empty
        Private ownerType As Type
        Private propertyType As Type
        Private validatorType As Type

        ' Nested Types
        <Serializable> _
        Private NotInheritable Class DependencyPropertyReference
            Implements IObjectReference
            ' Methods
            Public Function GetRealObject(ByVal context As StreamingContext) As Object
                Return DependencyProperty.FromName(Me.name, Me.type)
            End Function


            ' Fields
            Private name As String
            Private type As Type
        End Class

        Private Class KnownDependencyProperty
            ' Methods
            Friend Sub New(ByVal dependencyProperty As DependencyProperty, ByVal propertyValidity As PropertyValidity)
                Me.dependencyProperty = dependencyProperty
                Me.propertyValidity = propertyValidity
            End Sub


            ' Fields
            Friend dependencyProperty As DependencyProperty
            Friend propertyValidity As PropertyValidity
        End Class

        Friend Enum PropertyValidity
            ' Fields
            Always = 2
            Reexecute = 1
            Uninitialize = 0
        End Enum
    End Class
End Namespace

